package com.jrauto.CarAppBackend.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jrauto.CarAppBackend.entity.dto.LoginRequest;
import com.jrauto.CarAppBackend.entity.dto.RegisterRequest;
import com.jrauto.CarAppBackend.entity.User;
import com.jrauto.CarAppBackend.entity.dto.BindPhoneDTO;
import com.jrauto.CarAppBackend.entity.vo.LoginVO;
import com.jrauto.CarAppBackend.entity.vo.UserPublicVO;
import com.jrauto.CarAppBackend.service.UserService;
import com.jrauto.CarAppBackend.mapper.UserMapper;
import com.jrauto.CarAppBackend.utils.JwtUtil;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
* @author huimin
* @description 针对表【users】的数据库操作Service实现
* @createDate 2025-06-25 11:01:18
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
    implements UserService {

    @Resource
    private UserMapper userMapper; // 注入 MyBatis Plus Mapper

    @Resource
    private PasswordEncoder passwordEncoder; // 注入 Spring Security 的密码编码器

    @Resource
    private AuthenticationManager authenticationManager; // 注入 Spring Security 的 AuthenticationManager

    @Resource
    private AccessTokenService accessTokenService;

    @Resource
    private JwtUtil jwtUtil; // 注入 JWT 工具类

    private final RestTemplate restTemplate = new RestTemplate();

    @Value("${wechat.appid}")
    private String appid;

    @Value("${wechat.secret}")
    private String secret;
    /**
     * 用户登录实现
     * @param request 登录请求 DTO (包含手机号和密码)
     * @return 登录成功后的 JWT Token
     */
    @Override
    public String login(LoginRequest request) {
        try {
            // 1. 使用 Spring Security 的 AuthenticationManager 进行认证
            // 这会调用 UserDetailsService (CustomUserDetailsService) 的 loadUserByUsername 方法，并比对密码
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(request.getPhone(), request.getPassword())
            );

            // 2. 认证成功后，获取用户详情 (UserDetails 对象)
            final UserDetails userDetails = (UserDetails) authentication.getPrincipal(); // 从认证对象获取 UserDetails

            // 3. 从数据库加载完整的用户实体，以便获取 ID 和角色等信息用于 JWT Claims
            User user = findUserByPhone(request.getPhone());
            if (user == null) {
                // 理论上这里不会发生，因为 loadUserByUsername 已经验证了用户存在
                throw new RuntimeException("用户不存在");
            }

            // 4. 生成 JWT Token
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", user.getId()); // 将用户ID放入 JWT 声明
            claims.put("role", user.getRole()); // 将用户角色放入 JWT 声明

            // 调用 JwtUtil 生成 Token，传入手机号作为 subject
            return jwtUtil.generateToken(userDetails.getUsername(), claims);
        } catch (BadCredentialsException e) {
            // 密码不匹配
            throw new RuntimeException("手机号或密码不正确");
        } catch (UsernameNotFoundException e) { // 捕获 CustomUserDetailsService 中抛出的用户不存在/锁定/删除异常
            throw new RuntimeException(e.getMessage());
        } catch (RuntimeException e) {
            // 其他业务逻辑错误
            throw e;
        } catch (Exception e) {
            // 其他未知异常
            throw new RuntimeException("登录失败，服务器内部错误: " + e.getMessage());
        }
    }

    /**
     * 微信用户手机号登录或自动注册
     * @return 登录成功的用户登录凭证和用户信息
     */
    @Override
    @Transactional // 确保注册和登录的原子性
    public LoginVO wechatLoginPhone(String code1,String code2) {
        // 1. 调用 getPhoneNumber 方法，通过 code 获取手机号
        String openid = getOpenid(code2);
        System.out.println("openid:" + openid);
        String purePhoneNumber = getPhoneNumber(code1);
        if (purePhoneNumber == null) {
            throw new RuntimeException("获取手机号失败");
        }

        // 2. 根据手机号查询用户，看是否已注册
        User user = findUserByPhone(purePhoneNumber);
//        boolean isNewUser = false;

        // 3. 如果用户不存在，则执行自动注册
        if (user == null) {
//            isNewUser = true;
            user = new User();
            user.setPhone(purePhoneNumber);
            // 自动生成一个默认密码，用于可能需要的手机号密码登录（可选，取决于您的业务）
            user.setPassword(passwordEncoder.encode("default_password"));
            // 自动生成随机昵称
            user.setNickname("车主" + String.format("%04d", new Random().nextInt(10000)));
            user.setRole(0); // 设置默认角色为普通用户
            user.setStatus(1); // 默认状态为正常
            user.setOpenid(openid);
            // 保存新用户
            boolean saveSuccess = save(user);
            if (!saveSuccess) {
                throw new RuntimeException("自动注册失败");
            }
        }
        user.setOpenid(openid);
        // 4. 用户存在或已自动注册，为其生成 JWT Token
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("role", user.getRole());

        // 将手机号作为JWT的subject
        String token = jwtUtil.generateToken(user.getPhone(), claims);

        // 5. 构建并返回 LoginVO
        UserPublicVO userPublicVO = new UserPublicVO();
        BeanUtils.copyProperties(user, userPublicVO);
        userPublicVO.setPhone(user.getPhone());

        return new LoginVO(token, userPublicVO);
    }
    /**
     * 根据 code 获取用户手机号。
     * @param code 前端传来的获取手机号凭证
     * @return purePhoneNumber 用户手机号（不带区号）
     */
    public String getPhoneNumber(String code) {
        // 1. 获取 access_token。
        String accessToken = accessTokenService.getAccessToken();
        if (accessToken == null) {
            throw new RuntimeException("Access token is not available.");
        }
        // 2. 构造请求 URL。
        String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
        // 3. 构造请求头，指定 Content-Type 为 application/json。
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 4. 构造请求体，将 code 封装为 Map。
        // 5. 将请求体和请求头封装到 HttpEntity 中。
        HttpEntity<String> entity = new HttpEntity<>("{\"code\":\"" + code + "\"}", headers);
        try {
            // 6. 发送 POST 请求。
            String response = restTemplate.postForObject(url,entity, String.class);
            System.out.println(response);
            JSONObject jsonResponse = JSON.parseObject(response);
            // 5. 检查返回的错误码。
            if (jsonResponse.getInteger("errcode") == 0) {
                // 6. 解析 phone_info 对象，提取 purePhoneNumber。
                JSONObject phoneInfo = jsonResponse.getJSONObject("phone_info");
                return phoneInfo.getString("purePhoneNumber");
            } else {
                // 7. 如果返回错误，抛出异常并附带微信返回的错误信息。
                String errMsg = jsonResponse.getString("errmsg");
                System.err.println("Failed to get phone number: " + errMsg);
                throw new RuntimeException("获取手机号失败：" + errMsg);
            }
        } catch (Exception e) {
            System.err.println("Error while getting phone number: " + e.getMessage());
            throw new RuntimeException("获取手机号失败");
        }
    }



    /**
     * 用户注册实现
     *
     * @param request 注册请求 DTO
     * @return 注册成功的用户对象
     */
    @Override
    @Transactional // 开启事务，确保数据一致性
    public User registerUser(RegisterRequest request) {

        // 1. 检查手机号是否已存在
        User existingUserByPhone = findUserByPhone(request.getPhone());
        if (existingUserByPhone != null) {
            throw new RuntimeException("手机号已被注册"); // 抛出运行时异常，Controller 捕获并返回错误
        }

        // 2. 如果邮箱不为空，检查邮箱是否已存在
        if (request.getEmail() != null && !request.getEmail().isEmpty()) {
            LambdaQueryWrapper<User> emailQueryWrapper = new LambdaQueryWrapper<>();
            emailQueryWrapper.eq(User::getEmail, request.getEmail());
            User existingUserByEmail = userMapper.selectOne(emailQueryWrapper);
            if (existingUserByEmail != null) {
                throw new RuntimeException("邮箱已被注册");
            }
        }
        // 3. 如果openid不为空，检查openid是否已存在
        if (getOpenid(request.getCode())  != null && !getOpenid(request.getCode()).isEmpty()) {
            LambdaQueryWrapper<User> openidQueryWrapper = new LambdaQueryWrapper<>();
            openidQueryWrapper.eq(User::getOpenid, getOpenid(request.getCode()));
            User existingUserByOpenid = userMapper.selectOne(openidQueryWrapper);
            if (existingUserByOpenid != null) {
                throw new RuntimeException("openid已被注册");
            }
        }

        // 3. 构建用户实体
        User user = new User();
        user.setPhone(request.getPhone());
        // 密码加密存储
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        // 设置可选字段，如果前端没有传，则为 null
        user.setGender(request.getGender() != null ? request.getGender() : 1); // 默认性别为男
        user.setEmail(request.getEmail());
        user.setOpenid(getOpenid(request.getCode()));
        user.setNickname(request.getNickname());
//        System.out.println("code:" + request.getCode());
//        System.out.println("openid:" + getOpenid(request.getCode()));

        // 设置默认值（根据您的数据库设计）
//        user.setRole("owner"); // 注册时默认角色为车主
//        user.setIsMember(false); // 默认非会员
//        user.setStatus(1); // 默认状态正常 (0锁定，1正常，2删除)
        // created_at 和 updated_at 会由数据库自动处理，如果需要 Java 赋值，可以在这里设置 LocalDateTime.now()

        // 4. 保存用户
        // save 方法来自 ServiceImpl，底层调用 BaseMapper 的 insert 方法
        boolean saveSuccess = save(user);
        if (!saveSuccess) {
            throw new RuntimeException("用户注册失败");
        }

        // 注册成功后，返回部分用户信息 (不包含密码)
        user.setPassword(null); // 安全考虑，不将加密后的密码返回
        return user;
    }

    /**
     * 根据手机号查询用户实现
     *
     * @param phone 手机号
     * @return 用户对象
     */
    @Override
    public User findUserByPhone(String phone) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);
        return userMapper.selectOne(queryWrapper);
    }


    /**
     * 根据微信OpenID查询用户
     *
     * @param openid 微信OpenID
     * @return 用户对象，如果不存在则返回null
     */
    @Override // 变更为公共方法
    public User findUserByOpenid(String openid) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getOpenid, openid);
        return userMapper.selectOne(queryWrapper);
    }

    /**
     * 根据用户标识（手机号或OpenID）查询用户
     * @param identifier 用户标识字符串
     * @return 用户对象
     */
    @Override
    public User findUserByIdentifier(String identifier) {
        User user = null;

        // 尝试判断 identifier 是手机号还是 openid
        if (identifier.matches("^1[3-9]\\d{9}$")) { // 简单的手机号正则判断
            user = findUserByPhone(identifier);
        }

        // 如果不是手机号，或者手机号查询不到，则尝试按 openid 查询
        if (user == null) {
            user = findUserByOpenid(identifier);
        }
        return user;
    }

    /**
     * 根据微信OpenID查询用户
     *
     * @param openid 微信OpenID
     * @return 用户对象，如果不存在则返回null
     */
//    private User findUserByOpenid(String openid) {
//        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(User::getOpenid, openid);
//        return userMapper.selectOne(queryWrapper);
//    }
    /**
     * 微信登录业务逻辑
     * 根据微信code获取openid，然后判断用户是否存在。
     * 如果存在则直接登录并返回用户信息和JWT Token。
     * 如果不存在则注册新用户（随机昵称，保存openid），然后登录并返回用户信息和JWT Token。
     * @param code 微信小程序登录凭证
     * @return 包含JWT Token和用户信息的响应DTO
     */
    @Override
    @Transactional // 确保注册和登录的原子性
    public LoginVO wechatLogin(String code) {
        // 1. 根据code获取openid
        String openid = getOpenid(code);
        if (openid == null || openid.isEmpty()) {
            throw new RuntimeException("无法获取微信OpenID，请重试");
        }

        User user = findUserByOpenid(openid);
        boolean isNewUser = false;

        // 2. 检查用户是否存在
        if (user == null) {
            // 用户不存在，进行注册
            user = new User();
            user.setOpenid(openid);
            // 随机生成昵称：房主 + 4位随机数
            user.setNickname("车主" + String.format("%04d", new Random().nextInt(10000)));
            user.setRole(0); // 默认普通用户
            user.setStatus(1); // 默认启用
            // 密码可以为空，因为是微信登录
            boolean saveSuccess = save(user);
            if (!saveSuccess) {
                throw new RuntimeException("微信用户注册失败");
            }
            isNewUser = true;
        }
        // 3. 生成JWT Token
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getId());
        claims.put("role", user.getRole());
        // 使用OpenID作为JWT的subject，或者如果用户有手机号，也可以用手机号
        // 这里为了统一，如果用户有手机号，仍然用手机号作为subject，否则用openid
        String jwtSubject = (user.getPhone() != null && !user.getPhone().isEmpty()) ? user.getPhone() : user.getOpenid();
        String token = jwtUtil.generateToken(jwtSubject, claims);

        // 4. 构建返回的DTO
        UserPublicVO userPublicVO = new UserPublicVO();
        // 拷贝属性，排除密码和openid等敏感信息
        BeanUtils.copyProperties(user, userPublicVO);
        userPublicVO.setPhone(user.getPhone()); // 确保手机号字段被拷贝，即使可能为null

        return new LoginVO(token, userPublicVO);
    }


    @Override
    public String getOpenid(String code) {
        String uri = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={code}&grant_type=authorization_code";
        RestTemplate restTemplate = new RestTemplate();
        // 封装参数，这里是HashMap
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("appid", appid);
        paramMap.put("secret", secret);
        paramMap.put("code", code);
        String result = restTemplate.getForObject(uri, String.class, paramMap);
        // 使用 FastJSON 解析
        JSONObject jsonObject = JSON.parseObject(result);
        // 获取 openid、session_key
        String openid = jsonObject.getString("openid");
        return openid;
    }



    /**
     * 修改用户密码
     * @param userId 当前用户ID
     * @param password 包含旧密码和新密码的请求DTO
     */
    @Override
    @Transactional
    public void changePassword(Integer userId, String password) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 验证旧密码
//        if (user.getPassword() == null || !passwordEncoder.matches(request.getOldPassword(), user.getPassword())) {
//            throw new RuntimeException("旧密码不正确");
//        }

        // 更新新密码
        user.setPassword(passwordEncoder.encode(password));
        boolean updateSuccess = updateById(user);
        if (!updateSuccess) {
            throw new RuntimeException("密码修改失败");
        }
    }

    /**
     * 绑定手机号
     * @param userId 当前用户ID
     * @param request 包含要绑定手机号的请求DTO
     */
    @Override
    @Transactional
    public void bindPhone(Integer userId, BindPhoneDTO request) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 检查手机号是否已被其他用户绑定
        User existingUserWithPhone = findUserByPhone(request.getPhone());
        if (existingUserWithPhone != null && !existingUserWithPhone.getId().equals(userId)) {
            throw new RuntimeException("该手机号已被其他用户绑定");
        }

        // 如果用户已绑定手机号，且与当前请求的手机号不同，则不允许直接绑定，需要先解绑
        if (user.getPhone() != null && !user.getPhone().equals(request.getPhone())) {
            throw new RuntimeException("您已绑定手机号，请先解绑后再进行绑定操作");
        }

        user.setPhone(request.getPhone());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        // 如果用户之前没有密码（例如微信注册用户），可以考虑在此处设置一个默认密码或要求用户设置
        // 或者在前端引导用户绑定手机号时设置密码
        // 这里假设如果绑定手机号，用户可能会通过手机号登录，所以需要有密码
        if (user.getPassword() == null || user.getPassword().isEmpty()) {
            // 简单处理：如果微信用户绑定手机号，但没有密码，可以生成一个随机密码或强制用户在前端设置
            // 这里为了简化，不做强制设置，但实际应用中需要考虑安全性
            // user.setPassword(passwordEncoder.encode("default_password")); // 示例：设置默认密码
        }

        boolean updateSuccess = updateById(user);
        if (!updateSuccess) {
            throw new RuntimeException("手机号绑定失败");
        }
    }

    /**
     * 解绑手机号
     * @param userId 当前用户ID
     */
    @Override
    @Transactional
    public void unbindPhone(Integer userId) {
        User user = userMapper.selectById(userId);
        System.out.println(user);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        if (user.getPhone() == null || user.getPhone().isEmpty()) {
            throw new RuntimeException("当前用户未绑定手机号，无需解绑");
        }

        // 解绑手机号时，需要考虑用户是否还有其他登录凭证（如OpenID）
        // 如果解绑手机号后，用户没有任何登录凭证，可能会导致无法登录
        if (user.getOpenid() == null || user.getOpenid().isEmpty()) {
            throw new RuntimeException("解绑手机号后，您将没有其他登录方式，请先绑定微信或设置密码");
        }
        user.setPhone("");
        boolean updateSuccess = updateById(user);
        if (!updateSuccess) {
            throw new RuntimeException("手机号解绑失败");
        }
    }

    /**
     * 绑定微信OpenID
     * @param userId 当前用户ID
     * @param code 包含微信code的请求DTO
     */
    @Override
    @Transactional
    public void bindWechat(Integer userId,String code) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        String openid = getOpenid(code);
        if (openid == null || openid.isEmpty()) {
            throw new RuntimeException("无法获取微信OpenID，请重试");
        }

        // 检查OpenID是否已被其他用户绑定
        User existingUserWithOpenid = findUserByOpenid(openid);
        if (existingUserWithOpenid != null && !existingUserWithOpenid.getId().equals(userId)) {
            throw new RuntimeException("该微信OpenID已被其他用户绑定");
        }

        // 如果用户已经绑定了OpenID，且与当前请求的OpenID不同，则不允许直接绑定
        if (user.getOpenid() != null && !user.getOpenid().isEmpty() && !user.getOpenid().equals(openid)) {
            throw new RuntimeException("您已绑定微信，如需更换请联系客服"); // 或者提供解绑微信的功能
        }

        user.setOpenid(openid);
        boolean updateSuccess = updateById(user);
        if (!updateSuccess) {
            throw new RuntimeException("微信绑定失败");
        }
    }

    /**
     * 修改用户昵称
     * @param userId 当前用户ID
     * @param nickname 包含新昵称的请求DTO
     */
    @Override
    @Transactional
    public void changeNickname(Integer userId,String nickname) {
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new RuntimeException("用户不存在");
        }

        // 检查新昵称是否为空或过长等，这里可以添加更多业务规则
        if (nickname == null || nickname.trim().isEmpty()) {
            throw new RuntimeException("昵称不能为空");
        }
        if (nickname.trim().length() > 16) { // 假设昵称最大长度为16
            throw new RuntimeException("昵称过长，最大支持16个字符");
        }

        user.setNickname(nickname.trim());
        boolean updateSuccess = updateById(user);
        if (!updateSuccess) {
            throw new RuntimeException("昵称修改失败");
        }
    }


    /**
     * 根据用户ID更新用户头像URL
     * @param userId 用户ID
     * @param avatarUrl 新的头像URL
     */
    public void updateUserAvatar(Integer userId, String avatarUrl) {
        User user  =  userMapper.selectById(userId);
        // 检查用户是否存在，如果不存在则抛出异常
//        if (user == null) {
//            throw new RuntimeException("User not found with id: " + userId);
//        }
        // 更新用户的头像字段
        user.setAvatar(avatarUrl);
        System.out.println("user:" + user);
        System.out.println("avatarUrl:" + avatarUrl);
        // 调用 Mapper 的更新方法，将更改持久化到数据库
        // 这里假设使用了 MyBatis-Plus，其更新方法为 updateById
        userMapper.updateById(user);
    }
}